package genetic_algorithm;

import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;

/**
 * Interface of the selection phase in a genetic algorithm
 */
public abstract class Selection {

	protected Population population; // population to select the chromosomes pairs from
	protected boolean remove;

	/**
	 * Constructor
	 * @param population population of current generation
	 * @param remove indicates if should remove parents after they are selected
	 */
	public Selection(Population population, boolean remove) {
		super();
		this.remove = remove;
		this.population = population;
	}
	
	public Selection(boolean remove) {
		this.remove = remove;
	}
	

	public void setPopulation(Population population) {
		this.population = population;
	}

	/**
	 * Chooses a pair of chromosomes from the population.
	 * The chosen chromosomes are removed from the population
	 * @param population current population of chromosomes
	 * @return indices in population of chromosomes pair for mating
	 */
	protected abstract Entry<Integer, Integer> selectPair();
	
	/**
	 * Selects the best chromosomes out of given population 
	 * according to their fitness function value.
	 * The chosen chromosomes are removed from the population
	 * @param population current population of chromosomes
	 * @return next pair of chromosomes from the population
	 */
	public Entry<Chromosome,Chromosome> getNextPair() {
		
		// check if population is exhausted
		if (population.getSize() == 0) {
			// no more pairs are possible
			return null;
		}
		
		// sort population
		population.sort(); // TODO NEW!!!
		
		// get next pair
		Entry<Integer, Integer> pairIndex = selectPair();
		Entry<Chromosome, Chromosome> pair = new AbstractMap.SimpleEntry<Chromosome, Chromosome>(
				population.getChromosome(pairIndex.getKey()), population.getChromosome(pairIndex.getValue()));
		
		// remove chosen chromosomes from the population
		if(remove)
		{
			int key  = pairIndex.getKey(), val = pairIndex.getValue();
			population.removeChromosome(key > val ? key : val);
			population.removeChromosome(key > val ? val : key);			
		}

		// return next pair of chromosomes for mating
		return pair;
	}

	/**
	 * Implementation of elitism:
	 * returns the top <code> rank </code> chromosomes, and removes 
	 * the worst <code> rank </code> chromosomes
	 * @param population current chromosomes population
	 * @param rank elite size
	 * @return best <code> rank </code> chromosomes and their fitness values
	 */
//	public List<Chromosome> getElite(int rank) {
	public List<Entry<Chromosome,Double>> getElite(int rank) {
		
		// fix rank to be even
		rank = ((rank % 2) == 0) ? rank : rank + 1;
		
		// initialize elite group
		List<Entry<Chromosome,Double>> elite = new ArrayList<Entry<Chromosome,Double>>(rank);
				
		// sort population according to fitness values
		population.sort();

		// get chromosomes from last to (last-rank)'th
		int limit = population.getSize() - rank-1;
		for (int i = population.getSize()-1; i > limit; --i) {
			elite.add(new AbstractMap.SimpleEntry<Chromosome, Double>(
					population.getChromosome(i), population
							.getFitnessOfChromosome(i)));
		}
		
		// remove chromosomes from first to rank'th
		for (int i = 0; i < rank; ++i) {
			population.removeChromosome(i);
		}
		
		// return elite found
		return elite;
	}

	public boolean isRemove() {
		return remove;
	}
}
